package Executor;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.swing.JOptionPane;

import Auxiliary.Sequence_Event;
import Auxiliary.Sequence_SplitInfo;
import Auxiliary.Sequence_State;
import Auxiliary.Sequence_StateObserver;
import Auxiliary.Sequence_Transition;
import Boundary.EasySM;
import Boundary.UpdateEventCondReact;
import Boundary.UpdateInputCD;
import Boundary.UpdateProject;
import Boundary.UpdateStateObserverInv;
import Boundary.UpdateStateTable;
import DataView.CDElementInfo;
import DataView.ClassInfo;
import DataView.EnumerationInfo;
import DataView.EventInfo;
import DataView.Sequence_Error;
import DataView.StateObserverInfo;
import Store.Attribute;
import Store.CDElement;
import Store.Class;
import Store.Element;
import Store.Enumeration;
import Store.Event;
import Store.Operation;
import Store.Project;
import Store.Row;
import Store.StateMachine;
import Store.StateObserver;
import calculator.Generator;
import calculator.Serialization;

public class Main 
{
	private enum whereAmI
	{
		welcome,
		home,
		classDiagram,
		StateObserverInvariant,
		EventConditionsReactions,
		StateTable,
		GenerateSM
	};
	
	public EasySM easySM = new EasySM(this);
	private Sequence_State states = null;
	private Sequence_Transition trans = new Sequence_Transition();
	public UpdateProject updateProject;
	public UpdateInputCD updateInputCD;
	public UpdateStateObserverInv updateStateObserverInv;
	public UpdateEventCondReact updateEventCondReact;
	public UpdateStateTable updateStateTable;	
	public Project OPEN_PRJ;
	public Invalidator invalidator = new Invalidator();
	public Generator _unnamed_Generator_;
	public whereAmI iAm=whereAmI.welcome;
	
	private int iState=0;
	private int iEvent=0;
	private int iCoRe=0;

	public void doNewProject(String aFName) 
	{
		OPEN_PRJ=new Project();
		OPEN_PRJ.mkProject(aFName);
		iAm=whereAmI.home;
		updateProject = new UpdateProject(this);
		updateProject.mkOpenProject(OPEN_PRJ.vis());
	}

	public void doOpenProject(String aFname) 
	{
		FileInputStream file=null;
		try {file = new FileInputStream(aFname);}
		catch (FileNotFoundException e) {failed();}
		passFile(file);
	}

	public void passFile(FileInputStream aF) {
		easySM.ok();
		try {OPEN_PRJ=Serialization.deserialize(aF);} catch (IOException e) {} catch (ClassNotFoundException e) {}
		iAm=whereAmI.home;
		updateProject= new UpdateProject(this);
		updateProject.mkOpenProject(OPEN_PRJ.vis());
	}

	public void failed() 
	{
		easySM.ko();
	}

	public void doOpenInputCD() 
	{
		iAm=whereAmI.classDiagram;
		updateInputCD = new UpdateInputCD(this);
		updateInputCD.mkUpdateInputCD(OPEN_PRJ.inputCD.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doAddCDElement(CDElementInfo CDElIn) 
	{
		if(CDElIn.getClass()==EnumerationInfo.class)
		{
			Enumeration newElem = new Enumeration();
			newElem.mkElement((EnumerationInfo)CDElIn);
			if(newElem.containedIn()!=null)
				invalidator.invalidateDependentsOf(newElem.containedIn());
			OPEN_PRJ.inputCD.addCDElement(newElem);
		}
		else 
		{
			Class newElem = new Class();
			for (Operation o : ((ClassInfo)CDElIn).list_Operation) {o.setOfClass(newElem);}
			for (Attribute a : ((ClassInfo)CDElIn).list_Attribute) {a.setOfClass(newElem);}
			newElem.mkElement((ClassInfo)CDElIn);
			if(newElem.containedIn()!=null)
				invalidator.invalidateDependentsOf(newElem.containedIn());
			OPEN_PRJ.inputCD.addCDElement(newElem);
		}
		
		updateInputCD.setInputCD(OPEN_PRJ.inputCD.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doDeleteCDElement(CDElement aEl) 
	{
		invalidator.invalidateDependentsOf(aEl);
		OPEN_PRJ.inputCD.deleteCDElement(aEl);
		updateInputCD.setInputCD(OPEN_PRJ.inputCD.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doUpdCDElement(CDElement aEl, CDElementInfo aNewElIN) 
	{
		invalidator.invalidateDependentsOf(aEl);
		OPEN_PRJ.inputCD.updCDElement(aEl, aNewElIN);
		updateInputCD.setInputCD(OPEN_PRJ.inputCD.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doSetContextClass(Class cl) 
	{
		OPEN_PRJ.inputCD.setContextClass(cl);
		OPEN_PRJ.clear();
		updateInputCD.setInputCD(OPEN_PRJ.inputCD.vis(), OPEN_PRJ.inputCD.types());
	}

	/**
	 * All subparts of el will be also validated
	 */
	public void doValidate(Element aEl) 
	{
		invalidator.validate(aEl);
		switch(iAm)
		{			
			case classDiagram : updateInputCD.setInputCD(OPEN_PRJ.inputCD.vis(), OPEN_PRJ.inputCD.types()); break;
			case StateObserverInvariant : updateStateObserverInv.setSOINV(OPEN_PRJ.stateObserverInv.vis(), OPEN_PRJ.inputCD.types()); break;
		};
		
		
	}

	public void doClose() 
	{
		switch(iAm)
		{
			case welcome : System.exit(0); break;
			
			case home :
			{
				iAm=whereAmI.welcome;
				try {Serialization.serialize(OPEN_PRJ);} 
				catch (IOException e) {e.printStackTrace();}				
				easySM.closeProject();
			}; break;
			
			case classDiagram :
			{
				Sequence_Error err=OPEN_PRJ.inputCD.check();
				if(err.isEmpty())
				{
					iAm=whereAmI.home;
					updateProject.setProject(OPEN_PRJ.vis());
				}
				else updateInputCD.correct(err);
			}; break;
			case StateObserverInvariant :
			{
				Sequence_Error err = OPEN_PRJ.stateObserverInv.check(OPEN_PRJ.eventCondReact);
				if (err.isEmpty())
				{
					iAm=whereAmI.home;
					updateProject.setProject(OPEN_PRJ.vis());
				}
				else updateStateObserverInv.correct(err);
			}; break;
			case EventConditionsReactions:
			{
				Sequence_Error err = OPEN_PRJ.eventCondReact.check(OPEN_PRJ.stateObserverInv);
				if(err.isEmpty())
				{
					iAm=whereAmI.home;
					updateProject.setProject(OPEN_PRJ.vis());
				}
				else {
					updateEventCondReact.correct(err);
				}
			}; break;
			case StateTable:
			{
				Sequence_Error err = OPEN_PRJ.stateTable.check();
				if (err.isEmpty())
				{
					iAm = whereAmI.home;
					updateProject.setProject(OPEN_PRJ.vis());
				}
				else {
					updateStateTable.correct(err);
				}
			}; break;
		};
	}

	public void doSave() 
	{
		try {Serialization.serialize(OPEN_PRJ);} 
		catch (IOException e) {e.printStackTrace();}	
	}
	
	public void doOpenSOINV() 
	{
		iAm=whereAmI.StateObserverInvariant; 
		updateStateObserverInv = new UpdateStateObserverInv(this);
		updateStateObserverInv.mkUpdateStateObserverInv(OPEN_PRJ.stateObserverInv.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doUpdStateObserver(StateObserver aEl, StateObserverInfo aNewElIN) 
	{
		invalidator.invalidateDependentsOf(aEl);
		OPEN_PRJ.stateObserverInv.updateSO(aEl, aNewElIN);
		updateStateObserverInv.setSOINV(OPEN_PRJ.stateObserverInv.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doDeleteStateObserver(StateObserver aSo) 
	{
		invalidator.invalidateDependentsOf(aSo);
		OPEN_PRJ.stateObserverInv.deleteStateObserver(aSo);
		updateStateObserverInv.setSOINV(OPEN_PRJ.stateObserverInv.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doAddStateObserver(StateObserverInfo soInf) 
	{
		StateObserver so = new StateObserver();
		so.mkElement(soInf);
		invalidator.invalidateSoInvAndDependents();
		OPEN_PRJ.stateObserverInv.addStateObserver(so);
		updateStateObserverInv.setSOINV(OPEN_PRJ.stateObserverInv.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doGenerateBasicSO() 
	{
		if(OPEN_PRJ.inputCD.context()!=null)
		{
			Sequence_StateObserver sos = new Sequence_StateObserver();
			sos = Generator.generateBasicSO(OPEN_PRJ.inputCD, OPEN_PRJ.inputCD.context());
			invalidator.invalidateSoInvAndDependents();
			OPEN_PRJ.stateObserverInv.addStateObservers(sos);
			updateStateObserverInv.setSOINV(OPEN_PRJ.stateObserverInv.vis(), OPEN_PRJ.inputCD.types());
		}
		else updateStateObserverInv.ko();
	}

	public void doOpenEVCR() 
	{		
		iAm=whereAmI.EventConditionsReactions;
		updateEventCondReact = new UpdateEventCondReact(this);
		updateEventCondReact.mkUpdateEventCondReact(OPEN_PRJ.eventCondReact.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doAddEvent(EventInfo aEl) {
		Event e = new Event();
		e.mkElement(aEl);
		invalidator.invalidateEvCondReactAndDependents();
		OPEN_PRJ.eventCondReact.events.add(e);
	    updateEventCondReact.setEVCR(OPEN_PRJ.eventCondReact.vis(), OPEN_PRJ.inputCD.types());
	    
	}

	public void doGenerateBasicEV() 
	{
		if(OPEN_PRJ.inputCD.context()!=null)
		{
			Sequence_Event evs = new Sequence_Event();
			evs = Generator.generateBasicEV(OPEN_PRJ.inputCD, OPEN_PRJ.inputCD.context());
			invalidator.invalidateEvCondReactAndDependents();
			OPEN_PRJ.eventCondReact.addEvents(evs);
			updateEventCondReact.setEVCR(OPEN_PRJ.eventCondReact.vis(), OPEN_PRJ.inputCD.types());
		}
		else updateEventCondReact.ko();
	}

	public void doDeleteEvent(Event aEl) 
	{
		OPEN_PRJ.eventCondReact.deleteEvent(aEl);
		updateEventCondReact.setEVCR(OPEN_PRJ.eventCondReact.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doUpdEvent(Event aEl, EventInfo aNewElIN) {
		OPEN_PRJ.eventCondReact.updateEvent(aEl, aNewElIN);
		updateEventCondReact.setEVCR(OPEN_PRJ.eventCondReact.vis(), OPEN_PRJ.inputCD.types());
	}

	public void doSelectUsedSO(Sequence_StateObserver aUsedSO) 
	{
		OPEN_PRJ.stateTable.setUsedSO(aUsedSO);
		invalidator.invalidateDependentsOfST();
		updateStateTable.generateStates();
	}

	public void doSplitCR(Sequence_SplitInfo aSplits) 
	{
		OPEN_PRJ.eventCondReact.split(aSplits);
		invalidator.invalidateDependentsOfST();
		updateStateTable.setST(OPEN_PRJ.stateTable.vis());
	}

	public void doOpenST()
	{
		iAm= whereAmI.StateTable;
		updateStateTable = new UpdateStateTable(this);
		updateStateTable.mkUpdateStateTable(OPEN_PRJ.stateTable.vis(), OPEN_PRJ.stateObserverInv.getAll(), OPEN_PRJ.eventCondReact.vis());
	}

	public void doGenerateStates() 
	{
		OPEN_PRJ.stateTable.setROW(Generator.generateST(OPEN_PRJ.stateTable.used));
		updateStateTable.setST(OPEN_PRJ.stateTable.vis());
	}

	public void doDefineState(Row aRow, String aName) 
	{
		OPEN_PRJ.stateTable.defineState(aRow, aName);
		updateStateTable.setST(OPEN_PRJ.stateTable.vis());
	}

	public void doSplitCR() {
		updateStateTable.setEVCR(OPEN_PRJ.eventCondReact.vis());
	}

	public void doValidateST() 
	{
		invalidator.validateST();
		updateStateTable.setST(OPEN_PRJ.stateTable.vis()); 
	}

	public void doGenerateSM() 
	{
		states=Generator.generateStates(OPEN_PRJ.stateTable);
		
		if(!states.isEmpty() && !OPEN_PRJ.eventCondReact.events.getAll().isEmpty() && !OPEN_PRJ.eventCondReact.get(iEvent).getInfo().getCondReact().isEmpty())
			updateProject.examine(OPEN_PRJ.stateTable.row.find(states.get(iState).getName()) ,OPEN_PRJ.eventCondReact.events.get(iEvent).getInfo().getCondReact().get(iCoRe).getCond(), OPEN_PRJ.eventCondReact.events.get(iEvent).getInfo().getCondReact().get(iCoRe).getReact());
	}

	public void doGenterateSM_aux()
	{
		boolean ok = true;
		for(Store.Transition t : trans.getAll())
			if(t.source==null || t.target==null);
				ok=false;
		if(ok || trans.getAll().isEmpty())
		{
		OPEN_PRJ.setSM(new StateMachine(trans,states));
		Sequence_StateObserver temp = new Sequence_StateObserver();
		for (StateObserver s : OPEN_PRJ.stateObserverInv.list_stateObserver.getAll())
			if(OPEN_PRJ.stateTable.used.trova(s.getName())==null)
				temp.add(s);	
		
	    OPEN_PRJ.setUotputCD(Generator.generateOutputCD(OPEN_PRJ.inputCD, temp, OPEN_PRJ.eventCondReact.events));
	    invalidator.validateSM();
	    
	    updateProject.done();
		}
		else 
		{
			JOptionPane.showMessageDialog(updateProject.home.root.container, "You chose an illegal target in one or more transition. Retry to generate State Machine", "Error", JOptionPane.ERROR_MESSAGE);
			updateProject.setProject(OPEN_PRJ.vis());
		}
	}
	
	public void ok(Row targetRow) 
	{
		trans.add(Generator.generateTransition( 
												states.get(iState), 
												OPEN_PRJ.eventCondReact.events.get(iEvent), 
												OPEN_PRJ.eventCondReact.events.get(iEvent).getInfo().getCondReact().get(iCoRe), 
												targetRow));
		doExamine_aux();
	}
	
	public void doExamine_aux()
	{
		if(OPEN_PRJ.eventCondReact.events.get(iEvent).getInfo().getCondReact().size()==++iCoRe)
		{
			iCoRe=0;
			if(OPEN_PRJ.eventCondReact.events.size()==++iEvent)
			{
				iEvent=0;
				if(states.size()==++iState)
					iState=-1;
			}
		}
		if(iState==-1)
			{iState=0; doGenterateSM_aux();}
		else
			if(OPEN_PRJ.eventCondReact.events.get(iEvent).getInfo().getCondReact().get(iCoRe).getCondUsed().getDescription().compareTo("")!=0 && OPEN_PRJ.eventCondReact.events.get(iEvent).getInfo().getCondReact().get(iCoRe).getReactUsed().getDescription().compareTo("")!=0)
				updateProject.examine(OPEN_PRJ.stateTable.row.find(states.get(iState).getName()) ,OPEN_PRJ.eventCondReact.events.get(iEvent).getInfo().getCondReact().get(iCoRe).getCondUsed(), OPEN_PRJ.eventCondReact.events.get(iEvent).getInfo().getCondReact().get(iCoRe).getReactUsed());
			else doExamine_aux();
	}
	
	public static void main(String Args[])
	{
		@SuppressWarnings("unused")
		Main program = new Main();
	}

	
}